#pragma once
#include <memory>
#include "DXContext.h"
#include "Helper.h"
#include "DataArray.h"
#include "enums.h"

namespace DXBase
{
	namespace D3D
	{

		public value struct VertexElementDesc
		{
		public:
			Platform::String^ SemanticName;
			UINT SemanticIndex;
			DXBase::Format Format;
			UINT InputSlot;
			UINT AlignedByteOffset;
			InputClassification InputSlotClass;
			UINT InstanceDataStepRate;
		};

		// class that describe input data layout, for VertexShaders
		public ref class VertexLayout sealed
		{
		internal:
			DataArray<VertexElementDesc>^ elements;

			void Fill(std::vector<D3D11_INPUT_ELEMENT_DESC>& dst, std::vector<char>& semanticNameBuffer)
			{
				// first create a char array for holding the "LPCSTR SemanticName"
				dst.reserve(elements->Size);
				int NC = 0;
				for (uint32 i = 0; i < elements->Size; i++)
				{
					auto sSN = elements->data[i].SemanticName;
					if (sSN) 
					{
						auto wcp = sSN->Data();
						auto N = sSN ? sSN->Length() : 0;
						for (uint32 j = 0; j < N; j++)
							semanticNameBuffer.push_back((char)wcp[j]);
						NC += N;
					}
					semanticNameBuffer.push_back((char)0);
					NC += 1;
				}
				// fill the destination array now
				int NS = 0;
				for (uint32 i = 0; i < elements->Size; i++)
				{
					D3D11_INPUT_ELEMENT_DESC desc = 
					{
						&semanticNameBuffer[NS],
						elements->data[i].SemanticIndex,
						makeDXGI(elements->data[i].Format),
						elements->data[i].InputSlot,
						elements->data[i].AlignedByteOffset,
						makeD3D(elements->data[i].InputSlotClass),
						elements->data[i].InstanceDataStepRate
					};
					dst.push_back(desc);
					auto sSN = elements->data[i].SemanticName;
					NS += 1 + (sSN ? sSN->Length() : 0);
				}
			}

		public:
			VertexLayout()
			{
				elements = ref new DataArray<VertexElementDesc>();
			}

			property Windows::Foundation::Collections::IVector<VertexElementDesc>^ Elements 
			{ Windows::Foundation::Collections::IVector<VertexElementDesc>^ get() { return elements; } }

			// add a PerVertexData element's description
			void Add(Platform::String^ semanticName, Format fmt, UINT inputSlot)
			{
				Add(semanticName, fmt, inputSlot, InputClassification::PerVertexData, 0);
			}
			// add a PerInstanceData element's description
			void Add(Platform::String^ semanticName, Format fmt, UINT inputSlot, UINT stepRate)
			{
				Add(semanticName, fmt, inputSlot, InputClassification::PerInstanceData, stepRate);
			}
			// add an element description
			void Add(Platform::String^ semanticName, Format fmt, UINT inputSlot, InputClassification iClassify, UINT stepRate)
			{
				UINT byteOffset = 0;
				UINT semIndex = 0;
				for (uint32 i = 0; i < elements->Size; i++)
				{
					if (elements->data[0].SemanticName == semanticName)
						semIndex ++;
					if (elements->data[i].InputSlot == inputSlot)
					{
						auto anOffset = elements->data[i].AlignedByteOffset + GetSize(elements->data[i].Format);
						byteOffset = max(byteOffset, anOffset);
					}
				}
				VertexElementDesc ved = 
				{
					semanticName,
					semIndex,
					fmt,
					inputSlot,
					byteOffset,
					iClassify,
					stepRate
				};
				elements->Append(ved);
			}
		};
	}
}